import {defineStore} from "pinia";
import { v4 as uuid_v4 } from 'uuid';
import {Ref, ref} from "vue";
import {mapPropsToForms} from "@/propsMap.tsx";
import {
    CommonHistoryData,
    ComponentData,
    OperationHistoryData,
    PositionHistoryData,
    UpdateComponentData
} from "@/types/editor.ts";
import {PageProps} from "@/types/props.ts";


export const testComponents: ComponentData[] = [
    // {id: uuid_v4(), name: 'l-text',isHidden: false, isLocked: false, layerName: '图层1', props: {text: 'hello', fontSize: '20px', color: '#f5222d'}},
    {id: uuid_v4(), name: 'l-text',isHidden: false, isLocked: false, layerName: '图层2', props: {top: '10px', left: '10px', text: 'world', fontSize: '30px', lineHeight: '1', textAlign: 'center', fontFamily: '', backgroundColor: '#f5222d'}},
    // {id: uuid_v4(), name: 'l-text',isHidden: false, isLocked: false, layerName: '图层3', props: {text: 'hello2', fontSize: '40px', actionType: 'url', url: 'https://www.baidu.com'}},
]
const pageDefaultProps: PageProps = { backgroundColor: '#ffffff', backgroundImage: '', backgroundRepeat: 'no-repeat', backgroundSize: 'cover', height: '560px' }

const useEditorStore = defineStore('editor', () => {
    const components: Ref<ComponentData[]> = ref(testComponents)
    const page = ref({
        props: pageDefaultProps,
        title: "未命名页面",
    })
    const currentElement: Ref<ComponentData | undefined> = ref(undefined)
    const copiedElement: Ref<ComponentData | undefined> = ref(undefined)

    const historyList: Array<Partial<OperationHistoryData & PositionHistoryData> | null> = Array.from({length: 100}, () => null)
    const historyIdx = ref(-1)
    const historyMaxIdx = ref(-1)

    let cachedOldHistory: ComponentData | null = null
    let timer = 0

    function pushHistory(type: string, idx: number, oldData: ComponentData | null, newData: ComponentData | null) {
        historyIdx.value = historyIdx.value + 1;
        historyMaxIdx.value = historyIdx.value
        historyList.splice(historyIdx.value % 100, 1, {type: type, idx: idx, oldData: oldData, newData: newData})
    }

    function debounce(callback: (...args: any[]) => void, delay: number = 1000) {
        return function(...args: any[]) {
            clearTimeout(timer)
            timer = window.setTimeout(() => {
                callback(...args)
            }, delay)
        }
    }

    function pushUpdateHistory(type: string, idx: number, oldData: ComponentData | null, newData: ComponentData | null) {
        pushHistory(type, idx, oldData, newData)
        cachedOldHistory = null
    }

    function pushUpdateHistoryDebounce(type: string, idx: number, oldData: ComponentData | null, newData: ComponentData | null) {
        if (!cachedOldHistory) {
            cachedOldHistory = oldData
        }
        debounce(pushUpdateHistory)(type, idx, cachedOldHistory, newData)
    }

    function addComponent(component: ComponentData) {
        component.id = uuid_v4()
        component.layerName = `图层${components.value.length + 1}`
        components.value.push({isHidden: false, isLocked: false, ...component})
        pushHistory('add', components.value.length - 1, null, JSON.parse(JSON.stringify(component)))
    }

    function removeComponent(id: string | null) {
        if (currentElement.value) {
            if (!id || currentElement.value.id === id) {
                id = currentElement.value.id
                currentElement.value = undefined
            }
        }
        const index = components.value.findIndex(c => c.id === id)
        pushHistory('del', index, components.value[index], null)
        components.value.splice(index, 1)
    }

    function updateComponent({id, key, value, isRoot}: Partial<UpdateComponentData>) {
        const component = getComponent(id)
        if (!component) return
        if (isRoot) {
            (component as any)[key as string] = value
            return;
        }
        const oldComponent = JSON.parse(JSON.stringify(component))
        const idx = components.value.indexOf(component);
        if (Array.isArray(key) && Array.isArray(value)) {
            key.forEach((keyName, index) => {
                const item = mapPropsToForms[keyName]
                component.props[keyName] = item && item.afterTransform ? item.afterTransform(value[index]) : value[index]
            })
        } else if (typeof key === 'string') {
            const item = mapPropsToForms[key]
            component.props[key] = item && item.afterTransform ? item.afterTransform(value) : value
        }
        const newComponent = JSON.parse(JSON.stringify(component))
        pushUpdateHistoryDebounce('update', idx, oldComponent, newComponent)
    }

    function moveComponent({direction, amount}: {direction: 'Up' | 'Down' | 'Left' | 'Right', amount: number}) {
        const component = currentElement.value
        if (!component) return
        const oldTop = parseInt(component.props.top || '0')
        const oldLeft = parseInt(component.props.left || '0')
        switch (direction) {
            case 'Up': {
                const newValue = oldTop - amount
                updateComponent({ key: 'top', value: newValue, id: component.id })
                break
            }
            case 'Down': {
                const newValue = oldTop + amount
                updateComponent({ key: 'top', value: newValue, id: component.id })
                break
            }
            case 'Left': {
                const newValue = oldLeft - amount
                updateComponent({ key: 'left', value: newValue, id: component.id })
                break
            }
            case 'Right': {
                const newValue = oldLeft + amount
                updateComponent({ key: 'left', value: newValue, id: component.id })
                break
            }
            default:
                break
        }
    }

    function copyComponent() {
        if (currentElement.value) {
            copiedElement.value = currentElement.value
        }
    }

    function pasteComponent() {
        if (copiedElement.value) {
            const newComponent = JSON.parse(JSON.stringify(copiedElement.value))
            addComponent(newComponent)
        }
    }

    function setActiveComponent(id: string | null) {
        if (!id) {
            currentElement.value = undefined
            return
        }
        currentElement.value = components.value.find((component) => component.id === id)
    }

    function updatePage({key, value}: {key: string, value: any}) {
        if (page.value.props) {
            const keyName = key as keyof PageProps
            const item = mapPropsToForms[keyName]
            page.value.props[keyName] = item && item.afterTransform ? item.afterTransform(value) : value
        }
    }

    function getComponent(id: string | undefined) {
        if (id) {
            return components.value.find((component) => component.id === id)
        }
        return currentElement.value
    }

    function checkUndoDisable() {
        return historyIdx.value < 0;
    }

    function checkRedoDisable() {
        return historyIdx.value == historyMaxIdx.value;
    }

    function undo() {
        if (checkUndoDisable()) {
            return;
        }
        clearTimeout(timer)
        const history = historyList[historyIdx.value] as CommonHistoryData
        historyIdx.value = historyIdx.value - 1
        switch (history.type) {
            case 'add': {
                components.value.splice(history.idx, 1)
                break
            }
            case 'del': {
                const delHistory = history as OperationHistoryData
                components.value.splice(history.idx, 0, delHistory.oldData as ComponentData)
                break
            }
            case 'update': {
                const updateHistory = history as OperationHistoryData
                components.value[history.idx] = updateHistory.oldData as ComponentData
                break
            }
            default:
                break
        }
    }

    function redo() {
        if (checkRedoDisable()) {
            return ;
        }
        clearTimeout(timer)
        historyIdx.value = historyIdx.value + 1
        const history = historyList[historyIdx.value]
        if (history) {
            const commonHistory = history as CommonHistoryData
            switch (history.type) {
                case 'add': {
                    const addHistory = history as OperationHistoryData
                    components.value.splice(commonHistory.idx, 0, addHistory.newData as ComponentData)
                    break
                }
                case 'del': {
                    components.value.splice(commonHistory.idx, 1)
                    break
                }
                case 'update': {
                    const updateHistory = history as OperationHistoryData
                    components.value[commonHistory.idx] = updateHistory.newData as ComponentData
                    break
                }
                default:
                    break
            }
        }
    }

    return {
        components,
        currentElement,
        copiedElement,
        page,
        historyIdx,
        historyMaxIdx,
        addComponent,
        removeComponent,
        updateComponent,
        moveComponent,
        copyComponent,
        pasteComponent,
        setActiveComponent,
        updatePage,
        checkUndoDisable,
        checkRedoDisable,
        undo,
        redo,
    }
})

export default useEditorStore